package JasPerl::Util::Throwable;

use 5.010;
use strict;
use warnings;

use parent qw(Exception::Class::Base);

use constant Fields => qw(cause);

sub new {
    my $class = shift;
    return $class->SUPER::new(message => $_[0], cause => $_[1])
        if @_ == 2;
    return $class->SUPER::new(message => "$_[0]", cause => $_[0])
        if @_ == 1 and ref($_[0]);
    return $class->SUPER::new(@_);
}

sub from {
    my ($class, $from) = @_;
    return $class->new($from);
}

sub description {
    return '';
}

sub full_message {
    my $self = shift;
    my $class = ref $self;
    my $message = $self->message;
    return length($message) ? "$class: $message" : $class;
}

sub get_cause {
    return $_[0]->{cause};
}

sub get_message {
    return $_[0]->message;
}

sub get_stack_trace {
    return [ $_[0]->trace->frames ];
}

sub print_stack_trace {
    my ($self, $out, $caption, $trace) = @_;

    $out ||= \*STDERR;
    $out->print($caption) if defined $caption;
    $out->print($self, " at ", $self->file, " line ", $self->line, "\n");

    my $nframes = $self->trace->frame_count;
    if ($trace && $trace->frame_count <= $nframes) {
        for (my $i = $trace->frame_count - 1; $i > 0; $i--) {
            my $f = $self->trace->frame($nframes - 1);
            my $g = $trace->frame($i);

            last if $f->package ne $g->package;
            last if $f->filename ne $g->filename;
            last if $f->line ne $g->line;

            $nframes--;
        }
    }

    for (my $i = 1; $i < $nframes; $i++) {
        $out->print("\t", $self->trace->frame($i)->as_string, "\n");
    }

    my $nleft = $self->trace->frame_count - $nframes;
    $out->print("\t... $nleft more\n") if $nleft > 0;

    if (my $cause = $self->get_cause()) {
        if (eval { $cause->isa(__PACKAGE__) }) {
            $cause->print_stack_trace($out, "Caused by: ", $self->trace);
        } else {
            $out->print("Caused by: $cause\n");
        }
    }
}

1;
